package roborally.model;
import java.lang.Math;
import java.util.*;
import be.kuleuven.cs.som.annotate.*;

@Value
public class Position {
	/**
	 * 
	 * @param 	xCo
	 * 			The x-coordinate to initialize the position on.
	 * @param 	yCo
	 * 			The y-coordinate to initialize the position on.
	 * @effect 	a new position initialized on (xCo,yCo)
	 * 			|setPosition(xCo,yCo)
	 */
	public Position(long xCo, long yCo)
	{
		this.x = xCo;
		this.y = yCo;
	}
	/** 
	 * a long variable that contains the x-coordinate
	 */
	private final long x;
	/** 
	 * a long variable that contains the y-coordinate
	*/
	private final long y;
	
	
	public long getX(){return x;}
	public long getY(){return y;}
	
	/**
	 * returns the Position's coordinates as a String
	 */
	public String toString()
	{
		return "co(" + x + ", " + y + ")";
	}
	/**
	 * 
	 * @param 	position
	 * 			A position.
	 * @return	Returns true if both x and y coordinates are respectively equal. If not, return false
	 * 			result == this.getX() == position.getX()) && (this.getY() == position.getY()
	 */
	@Override
	public boolean equals(Object position)
	{
		if(!(position instanceof Position)) return false;
		return (this.getX() == ((Position)position).getX()) && (this.getY() == ((Position)position).getY());
	}
	
	/**
	 * Override hashcode so that positions with the same x and y coordinate have the same hashcode.
	 */
	@Override
	public int hashCode(){
		return Integer.parseInt(""+getX()+getY());
	}
	/**
	 * Returns the angle a given position makes with the positve x axis
	 */
	public double getAngleWithXAxis()
	{
		//angle = Bgcos[(Ex . A)/(||Ex||*||A||)] with Ex pointvector (1,0) and A pointvector (getX(),getY())
		//thus (Ex . A) = 1*getX() + 0*getY() == getX() and ||Ex||*||A||= ||A|| hence ||Ex||==1
		double d = Math.sqrt( Math.pow(getX(), 2) + Math.pow(getY(), 2) );
		double angleInRad = Math.acos( getX() / d );
		double angle = Math.toDegrees(angleInRad);
		if(getY() < 0) return 360 - angle; //for when the positionvector has a negative y-component, the angle we are looking for is 360 - angle
		else return angle;
	}
	
	public Position getDifferenceVector(Position toPos)
	{
		return new Position( toPos.getX() - this.getX(), toPos.getY() - this.getY() );
	}
	
	public Double getDistance(){
		return Math.sqrt( Math.pow(getX(),2) + Math.pow(getY(),2) );
	}
	public Position getNearestPosition(List<Position> positions){
		if(positions == null || positions.isEmpty()) return null;
		Position nearestPos = positions.get(0);
		for(Position pos : positions){
			if(this.getDifferenceVector(pos).getDistance() < this.getDifferenceVector(nearestPos).getDistance())
				nearestPos = pos;
		}
		return nearestPos;
	}
	
	public boolean withinRectangleDefinedBy(Position firstPosition, Position secondPosition){
		boolean validX = false;
		//check x values
		if(firstPosition.getX() <= secondPosition.getX())
			validX = this.getX() >= firstPosition.getX() && this.getX() <= secondPosition.getX();
		else
			validX = this.getX() >= secondPosition.getX() && this.getX() <= firstPosition.getX();
		//check y values
		boolean validY = false;
		if(firstPosition.getY() <= secondPosition.getY())
			validY = this.getY() >= firstPosition.getY() && this.getY() <= secondPosition.getY();
		else
			validY = this.getY() >= secondPosition.getY() && this.getY() <= firstPosition.getY();
	
		return validX && validY;	
	}
}
